﻿using UnityEngine;
using UnityEngine.Playables;

/// <summary>
/// clip 剪辑混合
/// 剪辑混合是一条Track里的多个剪辑发生过渡时，根据各个剪辑的权重用户自己算出一个最终的值。这个过程Unity不会自动做，要撸代码。
/// 注意混合是Track层面的事情，不是Clip里的事，要遍历多个Clip来计算
/// </summary>
public class MoveClipMixerBehaviour: PlayableBehaviour
{
    private bool isFirstFrameFinsh = false; //第一帧是否已经播放完成
    public override void ProcessFrame(Playable playable, FrameData info, object playerData)
    {
         Transform trackBinding = playerData as Transform;
         
         if (trackBinding == null)
         {
             return;
         }
         
         int inputCount = playable.GetInputCount();
         Debug.Log("绑定对象是" + trackBinding.gameObject.name + "  input count = " + inputCount);
         float positionTotalWeight = 0f;
         float rotationTotalWeight = 0f;
         Vector3 blendedPosition = Vector3.zero;
         Quaternion blendedRotation = new Quaternion(0f,0f,0f,0f);
         
         for (int i = 0; i < inputCount; i++)
         {
             ScriptPlayable<MoveClipBehaviour> input = (ScriptPlayable<MoveClipBehaviour>)playable.GetInput(i);
             MoveClipBehaviour behaviour = input.GetBehaviour();
             if (behaviour.endLocation == null)
             {
                 return;
             }
             
             
             float inputWeight = playable.GetInputWeight(i); //获取轨道的权重
             Debug.Log("权重等于" + inputWeight);
             if (!behaviour.startLocation || !isFirstFrameFinsh)
             {//添加默认值
                 behaviour.startingPosition = trackBinding.position;
                 behaviour.statingRotation = trackBinding.rotation;
             }

             float deltaTime =(float) (input.GetTime() / input.GetDuration());
             float tweenProgress = behaviour.EvaluteCurrentCuve(deltaTime);
             if (behaviour.tweenPosition)
             {
                 positionTotalWeight += inputWeight;
                 blendedPosition +=
                     Vector3.Lerp(behaviour.startingPosition, behaviour.GetEndLocationPositon(), tweenProgress) *
                     inputWeight;
             }
             
             if (behaviour.tweenRotation)
             {
                 rotationTotalWeight += inputWeight;
                 Quaternion q = Quaternion.Lerp(behaviour.endLocation.rotation, behaviour.startLocation.rotation,
                     tweenProgress);
                 q = NormalizeQuaternion(q);
                 if (Quaternion.Dot(blendedRotation, q) < 0)
                 {
                     q = ScaleQuaternion(q, -1f);
                 }

                 q = ScaleQuaternion(q, inputWeight);
                 blendedRotation = AddQuaternions(blendedRotation, q);
             }
         }

         blendedPosition += trackBinding.position * (1 - positionTotalWeight);
         Quaternion weightRotation = ScaleQuaternion(trackBinding.rotation, 1 - rotationTotalWeight);
         blendedRotation = AddQuaternions(blendedRotation, weightRotation);

         trackBinding.position = blendedPosition;
         trackBinding.rotation = blendedRotation;

         isFirstFrameFinsh = true;

    }

    public override void OnPlayableDestroy(Playable playable)
    {
        isFirstFrameFinsh = false;
        base.OnPlayableDestroy(playable);
    }

    static Quaternion AddQuaternions (Quaternion first, Quaternion second)
    {
        first.w += second.w;
        first.x += second.x;
        first.y += second.y;
        first.z += second.z;
        return first;
    }
    static Quaternion ScaleQuaternion (Quaternion rotation, float multiplier)
    {
        rotation.w *= multiplier;
        rotation.x *= multiplier;
        rotation.y *= multiplier;
        rotation.z *= multiplier;
        return rotation;
    }
    static float QuaternionMagnitude (Quaternion rotation)
    {
        return Mathf.Sqrt ((Quaternion.Dot (rotation, rotation)));
    }
    static Quaternion NormalizeQuaternion (Quaternion rotation)
    {
        float magnitude = QuaternionMagnitude (rotation);

        if (magnitude > 0f)
            return ScaleQuaternion (rotation, 1f / magnitude);

        Debug.LogWarning ("Cannot normalize a quaternion with zero magnitude.");
        return Quaternion.identity;
    }
}